package config

import (
	"crypto/tls"
	"errors"
	"os"
	"shell/common"
	"shell/common/packages"

	"github.com/go-playground/validator/v10"
	"github.com/google/uuid"
	"gopkg.in/yaml.v3"
)

type RelayCfgYaml struct {
	Enable     bool `yaml:"enable"`
	ListenPort int  `yaml:"listen_port" validate:"gt=0,lt=65535"`
}

type UseRelayCfgYaml struct {
	Enable    bool   `yaml:"enable"`
	RelayAddr string `yaml:"relay_addr" validate:"required,udp4_addr"`
}

type ControlledCfgYaml struct {
	ProxyAddr  string           `yaml:"proxy_addr" validate:"required,udp4_addr"`
	CaBase64   string           `yaml:"ca_base64" validate:"required,base64"`
	CertBase64 string           `yaml:"cert_base64" validate:"required,base64"`
	KeyBase64  string           `yaml:"key_base64" validate:"required,base64"`
	UUID       string           `yaml:"uuid" validate:"required,uuid"`
	Relay      *RelayCfgYaml    `yaml:"relay"`
	UseRelay   *UseRelayCfgYaml `yaml:"use_relay"`
}

type ControlledCfg struct {
	ProxyAddr string
	TLS       *tls.Config
	UUID      uuid.UUID
	Relay     *RelayCfgYaml
	UseRelay  *UseRelayCfgYaml
}

func ParseConfig(cfgPath string) (*ControlledCfg, error) {
	content, err := os.ReadFile(cfgPath)
	if err != nil {
		return nil, err
	}
	var cfgYaml ControlledCfgYaml
	if err := yaml.Unmarshal(content, &cfgYaml); err != nil {
		return nil, err
	}
	v := validator.New()
	if err := v.Struct(cfgYaml); err != nil {
		return nil, err
	}
	tlsCfg, err := common.TlsCfg(cfgYaml.CaBase64, cfgYaml.CertBase64, cfgYaml.KeyBase64, false)
	if err != nil {
		return nil, err
	}
	id, err := uuid.Parse(cfgYaml.UUID)
	if err != nil {
		return nil, err
	}
	result := &ControlledCfg{
		ProxyAddr: cfgYaml.ProxyAddr,
		TLS:       tlsCfg,
		UUID:      id,
		Relay:     cfgYaml.Relay,
		UseRelay:  cfgYaml.UseRelay,
	}
	if result.Relay != nil && result.UseRelay != nil {
		if result.Relay.Enable && result.UseRelay.Enable {
			return nil, errors.New("error relay and use relay can't enable together")
		}
	}

	return result, nil
}

// 通过被控端配置生成保活请求，缺少IP地址、中继uuid
func (cfg *ControlledCfg) ToKeepaliveInfo() *packages.KeepaliveInfo {
	result := new(packages.KeepaliveInfo)
	result.UUID = cfg.UUID
	if cfg.Relay != nil {
		result.IsRelay = cfg.Relay.Enable
	} else {
		result.IsRelay = false
	}
	if cfg.UseRelay != nil {
		result.UseRelay = cfg.UseRelay.Enable
	} else {
		result.UseRelay = false
	}
	result.OperatingSystem = packages.GetOS()
	return result
}
